package edu.ccut.saturn.component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.osgi.framework.Bundle;

import edu.ccut.saturn.component.impl.SaturnValidateUseInfo;
import edu.ccut.saturn.component.impl.node.SaturnDataNodeInfo;
import edu.ccut.saturn.core.IConstants;
import edu.ccut.saturn.core.impl.SaturnBundleService;
import edu.ccut.saturn.core.impl.SaturnPropertiesService;

/**
 * 验证组件管理者，提供了单态实例维护加载的验证组件信息及运行时的验证组件实例。
 * <p>
 * <li>字段bundleValidateInfos：记录了每个bundle中的验证组件信息；
 * <li>字段validate：记录了通过全局键值标识的验证组件的运行时实例。
 * 
 * @author 谷钰
 * @author 温建
 * 
 * @version 1.0 2008.9.04
 * 
 * @see IValidate
 * @see IValidateUseInfo
 * 
 */
public class ValidateManager {

	private static final String LOGGER_KEY_BUNDLE_SERVICE = SaturnLoggerManager.LOGGER_KEY_BUNDLE_SERVICE;

	protected static ILogger logger = SaturnLoggerManager.getInstance()
			.getSaturnLogger(LOGGER_KEY_BUNDLE_SERVICE);

	private static final String KEY_SEPARATOR = "::";

	private static final String VALUEKEY_EXPRESSION_START = "${";

	private static final String VALUEKEY_EXPRESSION_END = "}";

	private static final String DATA_KEY_SEPARATOR = "_";

	private Map<String, IBundleValidateInfo> bundleValidateInfos;

	private Map<String, IValidate> validate;// key is validate full key

	private static ValidateManager instance;

	static {
		instance = new ValidateManager();
	}

	private ValidateManager() {
		this.bundleValidateInfos = new HashMap<String, IBundleValidateInfo>();
		this.validate = new HashMap<String, IValidate>();
	}

	/**
	 * 获得单态实例
	 * 
	 * @return
	 */
	public static ValidateManager getInstance() {
		return instance;
	}

	/**
	 * add BundleValidateInfo to manager
	 * 
	 * @param bundleInfo
	 */
	public void addBundleValidateInfo(IBundleInfo bundleInfo) {
		this.bundleValidateInfos.put(bundleInfo.getBundleSymbolicName(),
				bundleInfo.getBundleValidateInfo());

		logger.debug("add bundleValidateInfo key \""
				+ bundleInfo.getBundleSymbolicName() + "\"");
	}

	/**
	 * remove BundleValidateInfo by bundle symbolic name from manager
	 * 
	 * @param bundleSymbolicName
	 */
	public void removeBundleValidtateInfo(String bundleSymbolicName) {

		if (this.bundleValidateInfos.containsKey(bundleSymbolicName)) {
			IBundleValidateInfo bundleValidateInfo = this.bundleValidateInfos
					.get(bundleSymbolicName);

			for (String validateKey : bundleValidateInfo.getValidateInfoKeys()) {
				this.removeValidate(bundleSymbolicName, validateKey);
			}

			this.bundleValidateInfos.remove(bundleSymbolicName);
			logger.debug("remove bundleValidateInfo key is : \""
					+ bundleValidateInfo.getBundleSymbolicName() + "\"");

			return;
		}

		logger
				.warn("failed to remove bundleValidateInfo, because the bundle key : \""
						+ bundleSymbolicName + "\" is not valid");
	}

	/**
	 * Get a {@link IBundleValidateInfo} by symbolic name
	 * 
	 * @param bundleSymbolicName
	 * @return if symbolic name is not exist, return null.
	 */
	public IBundleValidateInfo getBundleValidateInfo(String bundleSymbolicName) {
		return this.bundleValidateInfos.get(bundleSymbolicName);
	}

	/**
	 * Get all bundle symbolic name Set
	 * 
	 * @return
	 */
	public Set<String> getBundleSymbolicNames() {
		return this.bundleValidateInfos.keySet();
	}

	/**
	 * Get a {@link IValidate} by full key.
	 * 
	 * @param validateFullKey
	 *            bundleSymbolicName + "::" + validateKey
	 * @return
	 * @throws SaturnComponentException
	 */
	public IValidate getValidate(String validateFullKey)
			throws SaturnComponentException {

		int index = validateFullKey.indexOf(KEY_SEPARATOR);

		if (index == -1 || index == 0 || index == validateFullKey.length() - 2) {
			throw new SaturnComponentException(validateFullKey
					+ " is not a valid key");
		}
		String bundleSymbolicName = validateFullKey.substring(0, index);
		String validateKey = validateFullKey.substring(index + 2);
		return this.getValidate(bundleSymbolicName, validateKey);
	}

	// public IValidate getValidate(IValidateInfo validateInfo)
	// throws SaturnComponentException {
	//
	// String validateFullKey = validateInfo.getKey();
	// int index = validateFullKey.indexOf(KEY_SEPARATOR);
	//
	// if (index == -1 || index == 0 || index == validateFullKey.length() - 2) {
	// throw new SaturnComponentException(validateFullKey
	// + " is not a valid key");
	// }
	//
	// if (this.validate.containsKey(validateFullKey)) {
	// return this.validate.get(validateFullKey);
	// }
	// IValidate validate = validateInfo.createValidate();
	//
	// if (validate != null) {
	// this.validate.put(validateFullKey, validate);
	// }
	// return validate;
	// }

	/**
	 * Get validate by bundle symbolic name and validate key
	 * 
	 * @param bundleSymbolicName
	 * @param validateKey
	 * @return
	 * @throws SaturnComponentException
	 */
	public IValidate getValidate(String bundleSymbolicName, String validateKey)
			throws SaturnComponentException {

		String validateFullKey = this.getValidateFullKey(bundleSymbolicName,
				validateKey);

		if (this.validate.containsKey(validateFullKey)) {
			return this.validate.get(validateFullKey);
		}

		IValidateInfo validateInfo = getValidateInfo(bundleSymbolicName,
				validateKey);

		if (validateInfo == null) {
			throw new SaturnComponentException(
					"can't find validateInfo with key = [" + validateKey
							+ "] in bundle with symbolicName = ["
							+ bundleSymbolicName + "]");
		}
		IValidate validate = validateInfo.createValidate();
		validate.setValidateKey(validateFullKey);

		if (validate != null) {
			this.validate.put(validateFullKey, validate);
		}
		return validate;
	}

	/**
	 * 获得验证组件信息根据bundle名称和验证组件的键值
	 * 
	 * @param bundleSymbolicName
	 * @param validateKey
	 * @return
	 * @throws SaturnComponentException
	 */
	public IValidateInfo getValidateInfo(String bundleSymbolicName,
			String validateKey) throws SaturnComponentException {

		IBundleValidateInfo bundleValidateInfo = this.bundleValidateInfos
				.get(bundleSymbolicName);

		if (bundleValidateInfo == null) {
			throw new SaturnComponentException(
					"can't find bundle with symbolicName = ["
							+ bundleSymbolicName + "]");
		}

		IValidateInfo validateInfo = bundleValidateInfo
				.getValidateInfo(validateKey);
		
		if ("true".equals(SaturnPropertiesService.getInstance().getProperty(
				IConstants.SATURN_COMPONENTINFO_LOADER_INSTRUCTION))) {

			if (validateInfo == null) {
				Bundle bundle = SaturnBundleService.getInstance().getBundle(bundleSymbolicName);
				validateInfo = ComponentFactory.getValidateInfo(bundle,	validateKey);
				bundleValidateInfo.addValidateInfo(validateInfo);
			}
		}

		return validateInfo;
	}

	/**
	 * 根据验证组件的全局键值获得加载的验证组件信息
	 * 
	 * @param validateFullKey
	 * @return
	 * @throws SaturnComponentException
	 */
	public IValidateInfo getValidateInfo(String validateFullKey)
			throws SaturnComponentException {

		int index = validateFullKey.indexOf(KEY_SEPARATOR);

		if (index == -1 || index == 0 || index == validateFullKey.length() - 2) {
			throw new SaturnComponentException(validateFullKey
					+ " is not a valid key");
		}
		String bundleSymbolicName = validateFullKey.substring(0, index);
		String validateKey = validateFullKey.substring(index + 2);
		return getValidateInfo(bundleSymbolicName, validateKey);
	}

	private void removeValidate(String bundleSymbolicName, String validateKey) {
		String validateFullKey = this.getValidateFullKey(bundleSymbolicName,
				validateKey);

		if (this.validate.containsKey(validateFullKey)) {
			this.validate.remove(validateFullKey);
		}
	}

	private String getValidateFullKey(String bundleSymbolicName,
			String validateKey) {

		return bundleSymbolicName + KEY_SEPARATOR + validateKey;
	}

	/**
	 * 获得一个action组件中的所有数据验证信息。 首先获得action组件的每个结点信息，进而获得每个结点记录的数据验证信息；
	 * 对SaturnData类型数据的验证要特殊处理，需要根据数据节点记录的数据组件的键值来找到对数据项的每个字段的验证信息；
	 * 最后把这些数据验证信息返回。
	 * 
	 * @param componentFullKey
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public List<IValidateUseInfo> getValidateUseInfos(String componentFullKey) {
		List<IValidateUseInfo> validateUseInfos = null;

		if (componentFullKey != null && isValidKey(componentFullKey)) {
			int index = componentFullKey.indexOf(KEY_SEPARATOR);
			String bundleSymbolicName = componentFullKey.substring(1, index);
			String componentKey = componentFullKey.substring(index + 2);
			IBundleComponentInfo bundleComponentInfo = ComponentManager
					.getInstance().getBundleComponentInfo(bundleSymbolicName);

			if (bundleComponentInfo != null) {
				IComponentInfo componentInfo = bundleComponentInfo
						.getComponentInfo(componentKey);

				try {
					validateUseInfos = getValidateUseInfos(componentInfo);
				} catch (SaturnComponentException e) {
					logger.error(
							"found error! when get validate use information!",
							e);
				}
			}
		}
		return validateUseInfos;
	}

	private List<IValidateUseInfo> getValidateUseInfos(
			IComponentInfo componentInfo) throws SaturnComponentException {

		List<IValidateUseInfo> validateUseInfos = new LinkedList<IValidateUseInfo>();
		List<SaturnDataNodeInfo> dataNodeInfos = new LinkedList<SaturnDataNodeInfo>();

		if (componentInfo instanceof ILogicComponentInfo) {
			ILogicComponentInfo logicComponentInfo = (ILogicComponentInfo) componentInfo;

			Set<String> nodeIds = logicComponentInfo.getNodeIdKeySet();

			for (String nodeId : nodeIds) {
				INodeInfo nodeInfo = logicComponentInfo.getNodeInfo(nodeId);

				if (nodeInfo != null) {

					if (nodeInfo instanceof SaturnDataNodeInfo) {
						dataNodeInfos.add((SaturnDataNodeInfo) nodeInfo);
						continue;
					}

					List<IValidateUseInfo> useInfos = this
							.getValidateUseInfos(nodeInfo.getParameterInfos());

					if (useInfos != null) {
						validateUseInfos.addAll(useInfos);
					}
				}
			}

			if (validateUseInfos != null && validateUseInfos.size() > 0) {
				this.transformValidateUseInfos(validateUseInfos, dataNodeInfos);
			}
		}

		return validateUseInfos;
	}

	private void transformValidateUseInfos(
			List<IValidateUseInfo> validateUseInfos,
			List<SaturnDataNodeInfo> dataNodeInfos)
			throws SaturnComponentException {

		List<IValidateUseInfo> needToAddValidateUseInfos = new LinkedList<IValidateUseInfo>();
		List<IValidateUseInfo> needToRemoveValidateUseInfos = new LinkedList<IValidateUseInfo>();

		for (IValidateUseInfo validateUseInfo : validateUseInfos) {
			String valueKey = validateUseInfo.getValueKey();
			String valueClassType = validateUseInfo.getValueClassType();

			if (SaturnData.class.getName().equals(valueClassType)
					|| SaturnData.class.getSimpleName().equals(valueClassType)
					|| ArrayList.class.getName().equals(valueClassType)
					|| List.class.getName().equals(valueClassType)
					|| LinkedList.class.getName().equals(valueClassType)
					|| ArrayList.class.getSimpleName().equals(valueClassType)
					|| List.class.getSimpleName().equals(valueClassType)
					|| LinkedList.class.getSimpleName().equals(valueClassType)) {

				List<IValidateInfo> validateInfos = validateUseInfo
						.getValidateInfos();

				if (validateInfos != null && validateInfos.size() > 0) {
					List<IValidateInfo> needToRemoveValidateInfos = new LinkedList<IValidateInfo>();

					for (IValidateInfo validateInfo : validateInfos) {
						List<String> validateArgsList = validateInfo.getArgs();
						ValidateRule validateRule = getValidateRule(validateArgsList);
						String validateKey = validateInfo.getKey();
						String classType = null;

						if (getValidateInfo(validateKey) != null) {
							classType = getValidateInfo(validateKey)
									.getClassTypeStr();

							if (classType.equals(SaturnDataDefaultValidate.class
									.getName())
									|| classType
											.equals(SaturnDataDefaultValidate.class
													.getSimpleName())) {

								for (SaturnDataNodeInfo saturnDataNodeInfo : dataNodeInfos) {

									if (valueKey.equals(saturnDataNodeInfo
											.getDataKey())) {

										String namespace = saturnDataNodeInfo
												.getSaturnDataNamespace();

										String dataType = saturnDataNodeInfo
												.getSaturnDataType();

										IDataInfo dataInfo = SaturnDataManager
												.getInstance()
												.getSaturnDataInfo(namespace,
														dataType);

										List<IValidateUseInfo> useInfos = this
												.getValidateUseInfos(dataInfo,
														saturnDataNodeInfo,
														validateRule);

										if (useInfos != null
												&& useInfos.size() > 0) {

											needToAddValidateUseInfos
													.addAll(useInfos);

										}
										needToRemoveValidateInfos
												.add(validateInfo);
									}
								}
							}
						}
					}
					for (IValidateInfo removeValidateInfo : needToRemoveValidateInfos) {
						validateInfos.remove(removeValidateInfo);
					}
				}

				if (validateUseInfo.getValidateInfos().size() == 0) {
					needToRemoveValidateUseInfos.add(validateUseInfo);
				}
			}
		}

		for (IValidateUseInfo validateUseInfo : needToAddValidateUseInfos) {
			validateUseInfos.add(validateUseInfo);
		}

		for (IValidateUseInfo validateUseInfo : validateUseInfos) {
			String valueKey = validateUseInfo.getValueKey();

			if (isExpression(valueKey)) {
				String newValueKey = this.transformValueKey(valueKey,
						dataNodeInfos);

				if (newValueKey != null) {
					validateUseInfo.setValueKey(newValueKey);
				}
			}

			List<IValidateInfo> validateInfos = validateUseInfo
					.getValidateInfos();

			if (validateInfos != null) {

				for (IValidateInfo validateInfo : validateInfos) {
					List<String> args = validateInfo.getArgs();

					if (args != null) {

						for (int i = 0; i < args.size(); ++i) {
							String arg = args.get(i);

							if (isExpression(arg)) {
								String newArg = this.transformValueKey(arg,
										dataNodeInfos);

								if (newArg != null) {
									args.set(i, newArg);
								}
							}
						}
					}
				}
			}
		}

		for (IValidateUseInfo useInfo : needToRemoveValidateUseInfos) {
			validateUseInfos.remove(useInfo);
		}
	}

	// 添加过滤include、exclude
	private List<IValidateUseInfo> getValidateUseInfos(IDataInfo dataInfo,
			SaturnDataNodeInfo dataNodeInfo, ValidateRule validateRule) {

		List<IValidateUseInfo> validateUseInfos = new LinkedList<IValidateUseInfo>();

		if (dataInfo != null) {

			for (String key : dataInfo.keySet()) {

				if (!validateRule.checkKeyNeedValidate(key)) {
					continue;
				}

				IDataItem dataItem = dataInfo.getDataItem(key);

				IValidateUseInfo validateUseInfo = dataItem
						.getValidateUseInfo();

				if (validateUseInfo != null
						&& validateUseInfo.isValidate() == true) {

					List<IValidateInfo> allValidateInfos = validateUseInfo
							.getValidateInfos();

					List<IValidateInfo> validateInfos = new ArrayList<IValidateInfo>();

					if (allValidateInfos != null) {

						for (IValidateInfo validateInfo : allValidateInfos) {
							String dataItemValidateKey = validateInfo.getKey();

							if (validateRule.isValidate(key,
									dataItemValidateKey)) {

								validateInfos.add(validateInfo);
							}
						}
					}

					if (validateInfos != null && validateInfos.size() != 0) {
						validateUseInfo.setValidateInfos(validateInfos);
						IValidateUseInfo newValidateUseInfo = new SaturnValidateUseInfo(
								validateUseInfo);

						String valueKey = newValidateUseInfo.getValueKey();

						if (dataNodeInfo.getPrefix() != null
								&& !"".equals(dataNodeInfo.getPrefix())) {

							newValidateUseInfo.setValueKey(dataNodeInfo
									.getPrefix()
									+ DATA_KEY_SEPARATOR + valueKey);
						}
						validateUseInfos.add(newValidateUseInfo);
					}
				}
			}
		}

		if (validateUseInfos != null && validateUseInfos.size() > 0) {
			return validateUseInfos;
		}
		return null;
	}

	private String transformValueKey(String valueKey,
			List<SaturnDataNodeInfo> dataNodeInfos) {

		valueKey = valueKey.substring(2);
		valueKey = valueKey.substring(0, valueKey.length() - 1);
		int index = -1;

		if ((index = valueKey.indexOf(".")) > 0) {
			String saturnDataKey = valueKey.substring(0, index);

			for (SaturnDataNodeInfo saturnDataNodeInfo : dataNodeInfos) {

				if (saturnDataKey.equals(saturnDataNodeInfo.getDataKey())) {
					saturnDataKey = saturnDataNodeInfo.getPrefix();
					break;
				}
			}
			String saturnDataValueKey = valueKey.substring(index + 1);
			return saturnDataKey + DATA_KEY_SEPARATOR + saturnDataValueKey;
		}
		return null;
	}

	private boolean isExpression(String valueKey) {
		if (valueKey != null && valueKey.startsWith(VALUEKEY_EXPRESSION_START)
				&& valueKey.endsWith(VALUEKEY_EXPRESSION_END)) {

			return true;
		}
		return false;
	}

	private List<IValidateUseInfo> getValidateUseInfos(
			List<IParameterInfo> parameterInfos) {

		List<IValidateUseInfo> validateUseInfos = new LinkedList<IValidateUseInfo>();

		if (parameterInfos != null) {
			for (IParameterInfo parameterInfo : parameterInfos) {
				if (parameterInfo.getValidateUses() != null
						&& parameterInfo.getValidateUses().isValidate() == true) {

					validateUseInfos.add(new SaturnValidateUseInfo(parameterInfo
							.getValidateUses()));
				}
			}
		}

		if (validateUseInfos.size() == 0) {
			return null;
		}

		return validateUseInfos;
	}

	private boolean isValidKey(String componentFullKey) {
		int index = 0;

		if (componentFullKey != null && componentFullKey.length() != 0) {
			index = componentFullKey.indexOf(KEY_SEPARATOR);
		}

		if (index == -1 || index == 0 || index == componentFullKey.length() - 2) {
			return false;
		}
		return true;
	}

	private ValidateRule getValidateRule(List<String> args) {
		if (args != null && args.size() >= 4) {
			return new ValidateRule(args.get(2).substring(1,
					(args.get(2).length() - 1)), args.get(3).substring(1,
					(args.get(3).length() - 1)));
		}
		return new ValidateRule(null, null);
	}

	public List<IValidateInfo> getAllValidateInfo() {
		List<IValidateInfo> allValidateInfos = new ArrayList<IValidateInfo>();

		for (String bundleSymbolicName : getBundleSymbolicNames()) {
			IBundleValidateInfo bundleValidateInfo = ValidateManager
					.getInstance().getBundleValidateInfo(bundleSymbolicName);

			allValidateInfos.addAll(bundleValidateInfo.getValidateInfos());
		}
		return allValidateInfos;
	}
}
